use elasticsearch::cat::{CatIndices, CatIndicesParts};
use elasticsearch::http::request::JsonBody;
use elasticsearch::indices::{IndicesClose, IndicesCloseParts, IndicesCreate, IndicesCreateParts, IndicesDelete, IndicesDeleteParts, IndicesGetAlias, IndicesGetAliasParts, IndicesGetMapping, IndicesGetMappingParts, IndicesGetSettings, IndicesGetSettingsParts, IndicesOpen, IndicesOpenParts, IndicesStats, IndicesStatsParts, IndicesUpdateAliases};
use elasticsearch::params::ExpandWildcards;
use serde_json::{json, Value};
use crate::api::client::client;
use crate::api::get_values;

pub fn conversion_status(status: Option<String>) -> ExpandWildcards {
    match status {
        None => {
            ExpandWildcards::All
        }
        Some(value) => {
            if value == "Open" {
                ExpandWildcards::Open
            } else if value == "Close" {
                ExpandWildcards::Closed
            } else {
                ExpandWildcards::All
            }
        }
    }
}

pub fn conversion_sort(sort: Option<String>) -> Option<String> {
    match sort {
        None => {
            None
        }
        Some(value) => {
            match value.as_str() {
                "默认排序" => None,
                "名称正序" => Some("index:asc".to_string()),
                "名称倒序" => Some("index:desc".to_string()),
                "大小正序" => Some("store.size:asc".to_string()),
                "大小倒序" => Some("store.size:desc".to_string()),
                "数量正序" => Some("docs.count:asc".to_string()),
                "数量倒序" => Some("docs.count:desc".to_string()),
                _ => None
            }
        }
    }
}

// GET /_cat/indices?format=json&pretty
// health:  green代表健康；yellow代表分配了所有主分片，但至少缺少一个副本，此时集群数据仍旧完整；red代表部分主分片不可用，可能已经丢失数据。
// pri：primary缩写，主分片数量
// rep：副分片数量
// docs.count： Lucene 级别的文档数量
// docs.deleted： 删除的文档
// store.size：全部分片大小（包含副本）
// pri.store.size：主分片大小
pub async fn cat_indices(index: Option<String>, expand_wildcards: ExpandWildcards, sort_option: Option<String>) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = match index {
                None => {
                    match sort_option {
                        None => {
                            CatIndices::new(client.transport(), CatIndicesParts::None).format("json").pretty(true)
                                .expand_wildcards(&[expand_wildcards]).send().await
                        }
                        Some(sort) => {
                            CatIndices::new(client.transport(), CatIndicesParts::None).format("json").pretty(true)
                                .s(&[&sort])
                                .expand_wildcards(&[expand_wildcards]).send().await
                        }
                    }
                }
                Some(value) => {
                    match sort_option {
                        None => {
                            CatIndices::new(client.transport(), CatIndicesParts::Index(&[&value]))
                                .expand_wildcards(&[expand_wildcards]).format("json").pretty(true).send().await
                        }
                        Some(sort) => {
                            CatIndices::new(client.transport(), CatIndicesParts::Index(&[&value]))
                                .s(&[&sort])
                                .expand_wildcards(&[expand_wildcards]).format("json").pretty(true).send().await
                        }
                    }
                }
            };
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

// GET [index]/_alias
pub async fn indices_aliases(index: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesGetAlias::new(client.transport(), IndicesGetAliasParts::Index(&[&index])).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

// GET [index]/_stats?filter_path=_all,_shards
pub async fn indices_stats(index: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesStats::new(client.transport(), IndicesStatsParts::Index(&[&index]))
                .filter_path(&["_all", "_shards"]).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

// GET [index]/_mapping
pub async fn indices_mapping(index: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesGetMapping::new(client.transport(), IndicesGetMappingParts::Index(&[&index])).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

// GET [index]/_setting?include_defaults
pub async fn indices_settings(index: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesGetSettings::new(client.transport(), IndicesGetSettingsParts::Index(&[&index]))
                .include_defaults(true).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}


// get _stats/docs,store
pub async fn indices_stats_docs_store() -> Option<Value> {
    match client().await {
        Ok(client) => {
            let parts = IndicesStatsParts::Metric(&["docs", "store"]);
            let result = client.indices().stats(parts).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn add_aliases(index: String, alias: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesUpdateAliases::<JsonBody<Value>>::new(client.transport())
                .body(json!(
                    {
                        "actions": [
                            {
                                "add": {
                                    "alias": alias,
                                    "index": index
                                }
                            }
                        ]
                    }
                )).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn delete_aliases(index: String, alias: String) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let result = IndicesUpdateAliases::<JsonBody<Value>>::new(client.transport())
                .body(json!(
                    {
                        "actions": [
                            {
                                "remove": {
                                    "alias": alias,
                                    "index": index
                                }
                            }
                        ]
                    }
                )).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn close_indices(indices: Vec<String>) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let indices_slice: Vec<&str> = indices.iter().map(|s| s.as_str()).collect();
            let indices_ref: &[&str] = &indices_slice[..];
            let result = IndicesClose::<JsonBody<Value>>::new(client.transport(), IndicesCloseParts::Index(indices_ref)).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn open_indices(indices: Vec<String>) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let indices_slice: Vec<&str> = indices.iter().map(|s| s.as_str()).collect();
            let indices_ref: &[&str] = &indices_slice[..];
            let result = IndicesOpen::<JsonBody<Value>>::new(client.transport(), IndicesOpenParts::Index(indices_ref)).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn remove_indices(indices: Vec<String>) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let indices_slice: Vec<&str> = indices.iter().map(|s| s.as_str()).collect();
            let indices_ref: &[&str] = &indices_slice[..];
            let result = IndicesDelete::new(client.transport(), IndicesDeleteParts::Index(indices_ref)).send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}

pub async fn create_index(index: String, shards_option: Option<String>, replicas_option: Option<String>, mapping_option: Option<String>) -> Option<Value> {
    match client().await {
        Ok(client) => {
            let shards = match shards_option {
                None => 1,
                Some(value) => { value.parse::<i32>().unwrap_or(1) }
            };
            let replicas = match replicas_option {
                None => 0,
                Some(value) => { value.parse::<i32>().unwrap_or(0) }
            };
            let mut body = json!({
                    "settings": {
                        "number_of_shards": shards,
                        "number_of_replicas": replicas
                    }
                });
            if let Some(mapping) = mapping_option {
                let mapping_result = serde_json::from_str(mapping.as_str());
                if mapping_result.is_ok() {
                    let mut obj = body.as_object().unwrap().clone();
                    obj.insert("mappings".to_string(), mapping_result.unwrap());
                    body = Value::Object(obj);
                }
            }
            let result = IndicesCreate::<JsonBody<Value>>::new(client.transport(), IndicesCreateParts::Index(&index))
                .body(body)
                .send().await;
            get_values(result).await
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}
